package name.gauravmadan.mdg.generator;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;

import name.gauravmadan.mdg.pim.Application;
import name.gauravmadan.mdg.pim.Component;
import name.gauravmadan.mdg.service.ModelDataService;
import name.gauravmadan.mdg.pim.Class;
import name.gauravmadan.mdg.psm.ArchitectureTemplate;

public class CodeGenerator {
	private ModelDataService service;
	public final String BASE_FOLDER="D:\\MDG_BASE";
	public void generate(Integer applicationId)
	{
		try
		{
			ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/resources/psm-data.xml");
			//Application app = (Application)context.getBean("test-app-pim");
			Application app = service.getApplicationById(applicationId);
			app.setComponentList(service.getComponentListByAppId(app.getId()));
			for (Component comp : app.getComponentList())
			{
				comp.setClassList(service.getClassListByCompId(comp.getId()));
				for (Class clazz : comp.getClassList())
				{
					clazz.setAttributeList(service.getAttributeListByClassId(clazz.getId()));
				}
			}
			ArchitectureTemplate arch = (ArchitectureTemplate)context.getBean("test-app-psm");
			
			// Create the Folder Structure for the application inside the BASE_FOLDER
			File base = new File(BASE_FOLDER);
			File appRoot = new File(base,app.getApplicationName());
			boolean appRootCreated = appRoot.mkdir();

			// Create other folders inside the app folder
			File sourceRoot = new File(appRoot,"src");
			File resourceRoot = new File(appRoot,"resource");
			File webInfRoot = new File(appRoot,"WEB-INF");
			
			sourceRoot.mkdir();
			resourceRoot.mkdir();
			webInfRoot.mkdir();
			
			// Now create the Application Namespace Structure
			String namespace = app.getNamespace().replace('.', '/');
			File namespaceSourceRoot = new File(sourceRoot, namespace);
			namespaceSourceRoot.mkdirs();
			
			// Now create the structure for components in source root
			File compRoot = null;
			for (Component comp : app.getComponentList())
	    	{
				compRoot = new File(namespaceSourceRoot, comp.getName().toLowerCase());
				compRoot.mkdir();
	    	}
			
			// Get Freemarker configuration
			Configuration cfg = new Configuration();
	        cfg.setDirectoryForTemplateLoading(new File("D:\\Apps\\eclipse\\gaurav-java-workspace\\ModelDrivenGenerator\\src\\main\\resources\\plugins"));
	        cfg.setObjectWrapper(new DefaultObjectWrapper());
	        
	        // Loop thru the plugins and get the templates
	        for (String plugin : arch.getPluginList())
	        {
	        	// Read the plugin.properties in this folder
	        	FileInputStream fis = new FileInputStream(new File("D:\\Apps\\eclipse\\gaurav-java-workspace\\ModelDrivenGenerator\\src\\main\\resources\\plugins\\" + plugin + "\\plugin.properties"));
	        	Properties pluginProperties = new Properties();
	        	pluginProperties.load(fis);
	        	
	        	// Read whether this plugin generates files at the APPLICATION level or CLASS level
	        	
	        	String pluginLevel = pluginProperties.getProperty("PLUGIN_LEVEL");
	        	String pluginType = pluginProperties.getProperty("PLUGIN_TYPE");
	        	
	        	File baseFolder =  null;
	        	if (pluginType.equals("SOURCE")){
	        		baseFolder = namespaceSourceRoot;
	        	}
	        	else if (pluginType.equals("RESOURCE"))
	        	{
	        		baseFolder = resourceRoot;
	        	}else if (pluginType.equals("WEBINFRESOURCE"))
	        	{
	        		baseFolder = webInfRoot;
	        	}
	        		
	        	if (pluginLevel.equals("CLASS_LEVEL"))
	        	{
	        		generateClassLevelCode(cfg, pluginProperties, app, baseFolder);
	        	}
	        	else if (pluginLevel.equals("APPLICATION_LEVEL"))
	        	{
	        		generateApplicationLevelCode(cfg, pluginProperties, app, baseFolder);
	        	}
	        }
		}catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	public void generateClassLevelCode(Configuration cfg, Properties pluginProperties, Application app, File baseFolder)
	{
		try
		{
	    	// Read the template.ftl and create the template
	    	
	    	Template template = cfg.getTemplate(pluginProperties.getProperty("PLUGIN_NAME") + "/template.ftl");
	        Map<String,Object> root = null;
	        Map<String,Object> componentMap = null;
	        File compRoot = null;
	        
	    	// Loop thru the classes in PSM
	    	for (Component comp : app.getComponentList())
	    	{
	    		componentMap = new HashMap<String,Object>();
	    		componentMap.put("name", comp.getName());
	    		
	    		// Get the current component's File Root
	    		compRoot = new File(baseFolder, comp.getName().toLowerCase());
	    		
	    		// In this component, create a folder for the plugin artifact
	    		File artifactRoot = new File(compRoot,pluginProperties.getProperty("PLUGIN_FOLDER"));
	    		artifactRoot.mkdirs();
	    		
	    		for(Class clazz : comp.getClassList())
	    		{
	    			OutputStream os = new FileOutputStream(new File(artifactRoot, clazz.getClassName() + pluginProperties.getProperty("PLUGIN_ARTIFACT_CLASS_SUFFIX")));
	    			root = new HashMap();
	    			root.put("appName", app.getApplicationName());
	    			root.put("appNamespace", app.getNamespace());
	    			root.put("component", componentMap);
	    			root.put("clazz", clazz);
	    			
	    			Writer out = new OutputStreamWriter(os);
	    	        template.process(root, out);
	    	        out.flush();
	    	        System.out.println();
	    		}
	    	}
		}catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	public void generateApplicationLevelCode(Configuration cfg, Properties pluginProperties, Application app, File baseFolder)
	{
		try
		{
	    	// Read the template.ftl and create the template
	    	
	    	Template template = cfg.getTemplate(pluginProperties.getProperty("PLUGIN_NAME") + "/template.ftl");
	        Map<String,Object> root = null;
 
			OutputStream os = new FileOutputStream(new File(baseFolder, pluginProperties.getProperty("PLUGIN_ARTIFACT_NAME")));
			root = new HashMap();
			root.put("app", app);
			
			Writer out = new OutputStreamWriter(os);
	        template.process(root, out);
	        out.flush();
		}catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	public ModelDataService getService() {
		return service;
	}

	public void setService(ModelDataService service) {
		this.service = service;
	}

	public static void main(String args[])
	{
		CodeGenerator cg = new CodeGenerator();
		cg.generate(1);
		
	}
}
